{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ISubpr_SSsiM"
      },
      "source": [
        "##### Copyright 2020 The TensorFlow Authors.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "3jTMb1dySr3V"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6DWfyNThSziV"
      },
      "source": [
        "# 모듈, 레이어 및 모델 소개\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/guide/intro_to_modules\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org에서 보기</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ko/guide/intro_to_modules.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab에서 실행하기</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ko/guide/intro_to_modules.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub에서 보기</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ko/guide/intro_to_modules.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">노트북 다운로드하기</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "v0DdlfacAdTZ"
      },
      "source": [
        "머신러닝 TensorFlow를 수행하려면 모델을 정의, 저장 및 복원해야 할 수 있습니다.\n",
        "\n",
        "모델은 추상적으로 다음과 같습니다.\n",
        "\n",
        "- 텐서에서 무언가를 계산하는 함수(**정방향 전달**)\n",
        "- 훈련에 대한 응답으로 업데이트할 수 있는 일부 변수\n",
        "\n",
        "이 가이드에서는 Keras의 표면 아래로 이동하여 TensorFlow 모델이 어떻게 정의되는지 확인합니다. TensorFlow가 변수와 모델을 수집하는 방법과 변수와 모델이 저장 및 복원되는 방법을 살펴봅니다.\n",
        "\n",
        "참고: Keras를 즉시 시작하려면 [Keras 가이드 모음](./keras/)을 참조하세요.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VSa6ayJmfZxZ"
      },
      "source": [
        "## 설정"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "goZwOXp_xyQj"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "from datetime import datetime\n",
        "\n",
        "%load_ext tensorboard"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yt5HEbsYAbw1"
      },
      "source": [
        "## TensorFlow에서 모델 및 레이어 정의하기\n",
        "\n",
        "대부분의 모델은 레이어로 구성됩니다. 레이어는 재사용할 수 있고 훈련 가능한 변수를 가진, 알려진 수학적 구조의 함수입니다. TensorFlow에서 Keras 또는 Sonnet과 같은 레이어 및 모델의 상위 수준 구현 대부분은 같은 기본 클래스인 `tf.Module`를 기반으로 구축됩니다.\n",
        "\n",
        "다음은 스칼라 텐서에서 동작하는 매우 간단한 `tf.Module`의 예입니다.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "alhYPVEtAiSy"
      },
      "outputs": [],
      "source": [
        "class SimpleModule(tf.Module):\n",
        "  def __init__(self, name=None):\n",
        "    super().__init__(name=name)\n",
        "    self.a_variable = tf.Variable(5.0, name=\"train_me\")\n",
        "    self.non_trainable_variable = tf.Variable(5.0, trainable=False, name=\"do_not_train_me\")\n",
        "  def __call__(self, x):\n",
        "    return self.a_variable * x + self.non_trainable_variable\n",
        "\n",
        "simple_module = SimpleModule(name=\"simple\")\n",
        "\n",
        "simple_module(tf.constant(5.0))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JwMc_zu5Ant8"
      },
      "source": [
        "모듈과 더 나아가 레이어는 \"객체\"에 대한 딥 러닝 용어입니다. 내부 상태와 해당 상태를 사용하는 메서드가 있습니다.\n",
        "\n",
        "[Python callable](https://stackoverflow.com/questions/111234/what-is-a-callable)처럼 동작하는 것 외에는`__call__`에 특별한 점은 없습니다. 원하는 함수로 모델을 호출할 수 있습니다.\n",
        "\n",
        "미세 조정 중 레이어 및 변수 고정을 포함하여 어떤 이유로든 변수의 훈련 가능성을 설정 및 해제할 수 있습니다.\n",
        "\n",
        "참고: \"`tf.Module`은 `tf.keras.layers.Layer` 및 ` tf.keras.Model`의 기본 클래스이므로 여기에 표시되는 모든 항목도 Keras에 적용됩니다. 역사적 호환성의 이유로 Keras 레이어는 모듈에서 변수를 수집하지 않으므로 모델은 모듈만 사용하거나 Keras 레이어만 사용해야 합니다. 그러나 아래 표시된 변수 검사 메서드는 어느 경우에나 모두 같습니다.\n",
        "\n",
        "`tf.Module`을 하위 클래스화함으로써 이 객체의 속성에 할당된 `tf.Variable` 또는 `tf.Module` 인스턴스가 자동으로 수집됩니다. 이를 통해 변수를 저장 및 로드할 수 있으며 `tf.Module` 모음을 만들 수도 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CyzYy4A_CbVf"
      },
      "outputs": [],
      "source": [
        "# All trainable variables\n",
        "print(\"trainable variables:\", simple_module.trainable_variables)\n",
        "# Every variable\n",
        "print(\"all variables:\", simple_module.variables)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nuSFrRUNCaaW"
      },
      "source": [
        "다음은 모듈로 구성된 2개 레이어 선형 레이어 모델의 예입니다.\n",
        "\n",
        "먼저 밀집 (선형) 레이어입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Efb2p2bzAn-V"
      },
      "outputs": [],
      "source": [
        "class Dense(tf.Module):\n",
        "  def __init__(self, in_features, out_features, name=None):\n",
        "    super().__init__(name=name)\n",
        "    self.w = tf.Variable(\n",
        "      tf.random.normal([in_features, out_features]), name='w')\n",
        "    self.b = tf.Variable(tf.zeros([out_features]), name='b')\n",
        "  def __call__(self, x):\n",
        "    y = tf.matmul(x, self.w) + self.b\n",
        "    return tf.nn.relu(y)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bAhMuC-UpnhX"
      },
      "source": [
        "다음은 두 개의 레이어 인스턴스를 만들고 적용하는 전체 모델입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "QQ7qQf-DFw74"
      },
      "outputs": [],
      "source": [
        "class SequentialModule(tf.Module):\n",
        "  def __init__(self, name=None):\n",
        "    super().__init__(name=name)\n",
        "\n",
        "    self.dense_1 = Dense(in_features=3, out_features=3)\n",
        "    self.dense_2 = Dense(in_features=3, out_features=2)\n",
        "\n",
        "  def __call__(self, x):\n",
        "    x = self.dense_1(x)\n",
        "    return self.dense_2(x)\n",
        "\n",
        "# You have made a model!\n",
        "my_model = SequentialModule(name=\"the_model\")\n",
        "\n",
        "# Call it, with random results\n",
        "print(\"Model results:\", my_model(tf.constant([[2.0, 2.0, 2.0]])))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "d1oUzasJHHXf"
      },
      "source": [
        "`tf.Module` 인스턴스는 `tf.Variable` 또는 할당된 `tf.Module` 인스턴스를 재귀적으로 자동으로 수집합니다. 이를 통해 단일 모델 인스턴스로 `tf.Module` 모음을 관리하고 전체 모델을 저장 및 로드할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JLFA5_PEGb6C"
      },
      "outputs": [],
      "source": [
        "print(\"Submodules:\", my_model.submodules)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6lzoB8pcRN12"
      },
      "outputs": [],
      "source": [
        "for var in my_model.variables:\n",
        "  print(var, \"\\n\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hoaxL3zzm0vK"
      },
      "source": [
        "### 변수 생성 연기하기\n",
        "\n",
        "여기에서 레이어에 대한 입력 및 출력 크기를 모두 정의해야 한다는 것을 알 수 있습니다. `w` 변수가 알려진 형상을 가지므로 할당할 수 있습니다.\n",
        "\n",
        "특정 입력 형상으로 모듈이 처음 호출될 때까지 변수 생성을 연기하면 입력 크기를 미리 지정할 필요가 없습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "XsGCLFXlnPum"
      },
      "outputs": [],
      "source": [
        "class FlexibleDenseModule(tf.Module):\n",
        "  # Note: No need for `in+features`\n",
        "  def __init__(self, out_features, name=None):\n",
        "    super().__init__(name=name)\n",
        "    self.is_built = False\n",
        "    self.out_features = out_features\n",
        "\n",
        "  def __call__(self, x):\n",
        "    # Create variables on first call.\n",
        "    if not self.is_built:\n",
        "      self.w = tf.Variable(\n",
        "        tf.random.normal([x.shape[-1], self.out_features]), name='w')\n",
        "      self.b = tf.Variable(tf.zeros([self.out_features]), name='b')\n",
        "      self.is_built = True\n",
        "\n",
        "    y = tf.matmul(x, self.w) + self.b\n",
        "    return tf.nn.relu(y)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8bjOWax9LOkP"
      },
      "outputs": [],
      "source": [
        "# Used in a module\n",
        "class MySequentialModule(tf.Module):\n",
        "  def __init__(self, name=None):\n",
        "    super().__init__(name=name)\n",
        "\n",
        "    self.dense_1 = FlexibleDenseModule(out_features=3)\n",
        "    self.dense_2 = FlexibleDenseModule(out_features=2)\n",
        "\n",
        "  def __call__(self, x):\n",
        "    x = self.dense_1(x)\n",
        "    return self.dense_2(x)\n",
        "\n",
        "my_model = MySequentialModule(name=\"the_model\")\n",
        "print(\"Model results:\", my_model(tf.constant([[2.0, 2.0, 2.0]])))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "49JfbhVrpOLH"
      },
      "source": [
        "이러한 유연성으로 인해 TensorFlow 레이어는 종종 입력 및 출력 크기가 아닌 `tf.keras.layers.Dense`에서와 같이 출력의 형상만 지정하면 됩니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JOLVVBT8J_dl"
      },
      "source": [
        "## 가중치 저장하기\n",
        "\n",
        "`tf.Module`을 [checkpoint](./checkpoint.ipynb)와 [SavedModel](./saved_model.ipynb)로 모두 저장할 수 있습니다.\n",
        "\n",
        "체크포인트는 단지 가중치(즉, 모듈 및 하위 모듈 내부의 변수 세트 값)입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "pHXKRDk7OLHA"
      },
      "outputs": [],
      "source": [
        "chkp_path = \"my_checkpoint\"\n",
        "checkpoint = tf.train.Checkpoint(model=my_model)\n",
        "checkpoint.write(chkp_path)\n",
        "checkpoint.write(chkp_path)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WXOPMBR4T4ZR"
      },
      "source": [
        "체크포인트는 데이터 자체와 메타데이터용 인덱스 파일이라는 두 가지 종류의 파일로 구성됩니다. 인덱스 파일은 실제로 저장된 항목과 체크포인트의 번호를 추적하는 반면 체크포인트 데이터에는 변수 값과 해당 속성 조회 경로가 포함됩니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jBV3fprlTWqJ"
      },
      "outputs": [],
      "source": [
        "!ls my_checkpoint*"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CowCuBTvXgUu"
      },
      "source": [
        "체크포인트 내부를 살펴보면 전체 변수 모음이 저장되고 변수 모음이 포함된 Python 객체별로 정렬되어 있는지 확인할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "o2QAdfpvS8tB"
      },
      "outputs": [],
      "source": [
        "tf.train.list_variables(chkp_path)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4eGaNiQWcK4j"
      },
      "source": [
        "분산 (다중 머신) 훈련 중에 변수 모음이 샤딩될 수 있으므로 번호가 매겨집니다(예: '00000-of-00001'). 하지만 이 경우에는 샤드가 하나만 있습니다.\n",
        "\n",
        "모델을 다시 로드하면 Python 객체의 값을 덮어씁니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "UV8rdDzcwVVg"
      },
      "outputs": [],
      "source": [
        "new_model = MySequentialModule()\n",
        "new_checkpoint = tf.train.Checkpoint(model=new_model)\n",
        "new_checkpoint.restore(\"my_checkpoint\")\n",
        "\n",
        "# Should be the same result as above\n",
        "new_model(tf.constant([[2.0, 2.0, 2.0]]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BnPwDRwamdfq"
      },
      "source": [
        "참고: 체크포인트는 긴 훈련 워크플로의 핵심이므로 `tf.checkpoint.CheckpointManager`는 체크포인트 관리를 훨씬 쉽게 만들어 주는 도우미 클래스입니다. 자세한 내용은 [가이드](./checkpoint.ipynb)를 참조하세요."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pSZebVuWxDXu"
      },
      "source": [
        "## 함수 저장하기\n",
        "\n",
        "TensorFlow는 [TensorFlow Serving](https://tensorflow.org/tfx) 및 [TensorFlow Lite](https://tensorflow.org/lite)에서와 같이 원래 Python 객체 없이 모델을 실행할 수 있으며 [TensorFlow Hub](https://tensorflow.org/hub)에서 훈련된 모델을 다운로드하는 경우에도 실행할 수 있습니다. .\n",
        "\n",
        "TensorFlow는 Python에 설명된 계산을 수행하는 방법을 알아야 하지만 **원본 코드는 없습니다**. 이를 위해 이전 [가이드](./intro_to_graphs.ipynb)에 설명된 <strong>그래프</strong>를 만들 수 있습니다.\n",
        "\n",
        "이 그래프에는 함수를 구현하는 연산 또는 *ops*가 포함됩니다.\n",
        "\n",
        "이 코드가 그래프로 실행되어야 함을 나타내기 위해 `@tf.function` 데코레이터를 추가하여 위 모델에서 그래프를 정의할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "WQTvkapUh7lk"
      },
      "outputs": [],
      "source": [
        "class MySequentialModule(tf.Module):\n",
        "  def __init__(self, name=None):\n",
        "    super().__init__(name=name)\n",
        "\n",
        "    self.dense_1 = Dense(in_features=3, out_features=3)\n",
        "    self.dense_2 = Dense(in_features=3, out_features=2)\n",
        "\n",
        "  @tf.function\n",
        "  def __call__(self, x):\n",
        "    x = self.dense_1(x)\n",
        "    return self.dense_2(x)\n",
        "\n",
        "# You have made a model with a graph!\n",
        "my_model = MySequentialModule(name=\"the_model\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hW66YXBziLo9"
      },
      "source": [
        "여러분이 만든 모듈은 이전과 똑같이 동작합니다. 함수에 전달된 각 고유 서명은 별도의 그래프를 생성합니다. 자세한 내용은 [그래프 가이드](./intro_to_graphs.ipynb)를 참조하세요."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "H5zUfti3iR52"
      },
      "outputs": [],
      "source": [
        "print(my_model([[2.0, 2.0, 2.0]]))\n",
        "print(my_model([[[2.0, 2.0, 2.0], [2.0, 2.0, 2.0]]]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lbGlU1kgyDo7"
      },
      "source": [
        "TensorBoard 요약 내에서 그래프를 추적하여 그래프를 시각화할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zmy-T67zhp-S"
      },
      "outputs": [],
      "source": [
        "# Set up logging.\n",
        "stamp = datetime.now().strftime(\"%Y%m%d-%H%M%S\")\n",
        "logdir = \"logs/func/%s\" % stamp\n",
        "writer = tf.summary.create_file_writer(logdir)\n",
        "\n",
        "# Create a new model to get a fresh trace\n",
        "# Otherwise the summary will not see the graph.\n",
        "new_model = MySequentialModule()\n",
        "\n",
        "# Bracket the function call with\n",
        "# tf.summary.trace_on() and tf.summary.trace_export().\n",
        "tf.summary.trace_on(graph=True, profiler=True)\n",
        "# Call only one tf.function when tracing.\n",
        "z = print(new_model(tf.constant([[2.0, 2.0, 2.0]])))\n",
        "with writer.as_default():\n",
        "  tf.summary.trace_export(\n",
        "      name=\"my_func_trace\",\n",
        "      step=0,\n",
        "      profiler_outdir=logdir)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "gz4lwNZ9hR79"
      },
      "source": [
        "tensorboard를 실행하여 결과 추적을 확인합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "V4MXDbgBnkJu"
      },
      "outputs": [],
      "source": [
        "#docs_infra: no_execute\n",
        "%tensorboard --logdir logs/func"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Gjattu0AhYUl"
      },
      "source": [
        "![텐서 보드의 그래프 스크린 샷](images/tensorboard_graph.png)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SQu3TVZecmL7"
      },
      "source": [
        "### `SavedModel` 생성하기\n",
        "\n",
        "완전히 훈련된 모델을 공유하는 권장 방법은 `SavedModel`을 사용하는 것입니다. `SavedModel`에는 함수 모음과 가중치 모음이 모두 포함됩니다.\n",
        "\n",
        "방금 만든 모델을 저장할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Awv_Tw__WK7a"
      },
      "outputs": [],
      "source": [
        "tf.saved_model.save(my_model, \"the_saved_model\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "SXv3mEKsefGj"
      },
      "outputs": [],
      "source": [
        "# Inspect the in the directory\n",
        "!ls -l the_saved_model"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vQQ3hEvHYdoR"
      },
      "outputs": [],
      "source": [
        "# The variables/ directory contains a checkpoint of the variables \n",
        "!ls -l the_saved_model/variables"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xBqPop7ZesBU"
      },
      "source": [
        "`saved_model.pb` 파일은 함수형 <code>tf.Graph</code>를 설명하는 <a>프로토콜 버퍼</a>입니다.\n",
        "\n",
        "모델과 레이어는 실제로 이 표현을 생성한 클래스의 인스턴스를 만들지 않고도 이 표현에서 로드할 수 있습니다. 이는 대규모 또는 에지 기기에서 제공하는 것과 같이 Python 인터프리터가 없거나 또는 원하지 않는 상황 또는 원래 Python 코드를 사용할 수 없거나 사용하는 것이 실용적이지 않은 상황에서 바람직합니다.\n",
        "\n",
        "모델을 새 객체로 로드할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zRFcA5wIefv4"
      },
      "outputs": [],
      "source": [
        "new_model = tf.saved_model.load(\"the_saved_model\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-9EF3mT7i3qN"
      },
      "source": [
        "저장된 모델을 로드하여 생성된 `new_model`은 클래스 지식이 없는 내부 TensorFlow 사용자 객체입니다. `SequentialModule` 유형이 아닙니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "EC_eQj7yi54G"
      },
      "outputs": [],
      "source": [
        "isinstance(new_model, SequentialModule)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-OrOX1zxiyhR"
      },
      "source": [
        "이 새 모델은 이미 정의된 입력 서명에서 동작합니다. 이와 같이 복원된 모델에 더 많은 서명을 추가할 수 없습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "_23BYYBWfKnc"
      },
      "outputs": [],
      "source": [
        "print(my_model([[2.0, 2.0, 2.0]]))\n",
        "print(my_model([[[2.0, 2.0, 2.0], [2.0, 2.0, 2.0]]]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qSFhoMtTjSR6"
      },
      "source": [
        "따라서, `SavedModel`을 사용하면 `tf.Module`을 사용하여 TensorFlow 가중치와 그래프를 저장한 다음 다시 로드할 수 있습니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Rb9IdN7hlUZK"
      },
      "source": [
        "## Keras 모델 및 레이어\n",
        "\n",
        "이 시점까지 Keras에 대한 언급이 없습니다. `tf.Module` 위에 고유한 상위 수준 API를 빌드할 수 있습니다.\n",
        "\n",
        "이 섹션에서는 Keras가 `tf.Module`을 사용하는 방법을 살펴봅니다. Keras 모델에 대한 전체 사용자 가이드는 [Keras 가이드](keras/sequential_model.ipynb)에서 찾을 수 있습니다.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uigsVGPreE-D"
      },
      "source": [
        "### Keras 레이어\n",
        "\n",
        "`tf.keras.layers.Layer`는 모든 Keras 레이어의 기본 클래스이며 `tf.Module`에서 상속합니다.\n",
        "\n",
        "부모를 교체한 다음 `__call__`을 `call`로 변경하여 모듈을 Keras 레이어로 변환할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "88YOGquhnQRd"
      },
      "outputs": [],
      "source": [
        "class MyDense(tf.keras.layers.Layer):\n",
        "  # Adding **kwargs to support base Keras layer arguemnts\n",
        "  def __init__(self, in_features, out_features, **kwargs):\n",
        "    super().__init__(**kwargs)\n",
        "\n",
        "    # This will soon move to the build step; see below\n",
        "    self.w = tf.Variable(\n",
        "      tf.random.normal([in_features, out_features]), name='w')\n",
        "    self.b = tf.Variable(tf.zeros([out_features]), name='b')\n",
        "  def call(self, x):\n",
        "    y = tf.matmul(x, self.w) + self.b\n",
        "    return tf.nn.relu(y)\n",
        "\n",
        "simple_layer = MyDense(name=\"simple\", in_features=3, out_features=3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nYGmAsPrws--"
      },
      "source": [
        "Keras 레이어에는 다음 섹션에서 설명하는 몇 가지 부기(bookkeeping)를 수행한 다음 `call()`을 호출하는 고유한 `__call__`이 있습니다. 기능에 변화가 없어야 합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nIqE8wOznYKG"
      },
      "outputs": [],
      "source": [
        "simple_layer([[2.0, 2.0, 2.0]])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tmN5vb1K18U1"
      },
      "source": [
        "### `build` 단계\n",
        "\n",
        "앞서 언급했듯이, 입력 형상이 확실해질 때까지 변수를 생성하기 위해 기다리는 것이 많은 경우 편리합니다.\n",
        "\n",
        "Keras 레이어에는 레이어를 정의하는 방법에 더 많은 유연성을 제공하는 추가 수명 주기 단계가 있습니다. `build()` 함수에서 정의됩니다.\n",
        "\n",
        "`build`는 정확히 한 번만 호출되며 입력 형상으로 호출됩니다. 일반적으로 변수(가중치)를 만드는 데 사용됩니다.\n",
        "\n",
        "위의 `MyDense` 레이어를 입력 크기에 맞게 다시 작성할 수 있습니다.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4YTfrlgdsURp"
      },
      "outputs": [],
      "source": [
        "class FlexibleDense(tf.keras.layers.Layer):\n",
        "  # Note the added `**kwargs`, as Keras supports many arguments\n",
        "  def __init__(self, out_features, **kwargs):\n",
        "    super().__init__(**kwargs)\n",
        "    self.out_features = out_features\n",
        "\n",
        "  def build(self, input_shape):  # Create the state of the layer (weights)\n",
        "    self.w = tf.Variable(\n",
        "      tf.random.normal([input_shape[-1], self.out_features]), name='w')\n",
        "    self.b = tf.Variable(tf.zeros([self.out_features]), name='b')\n",
        "\n",
        "  def call(self, inputs):  # Defines the computation from inputs to outputs\n",
        "    return tf.matmul(inputs, self.w) + self.b\n",
        "\n",
        "# Create the instance of the layer\n",
        "flexible_dense = FlexibleDense(out_features=3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Koc_uSqt2PRh"
      },
      "source": [
        "이 시점에서는 모델이 빌드되지 않았으므로 변수가 없습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "DgyTyUD32Ln4"
      },
      "outputs": [],
      "source": [
        "flexible_dense.variables"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-KdamIVl2W8Y"
      },
      "source": [
        "함수를 호출하면 적절한 크기의 변수가 할당됩니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "IkLyEx7uAoTK"
      },
      "outputs": [],
      "source": [
        "# Call it, with predictably random results\n",
        "print(\"Model results:\", flexible_dense(tf.constant([[2.0, 2.0, 2.0], [3.0, 3.0, 3.0]])))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Swofpkrd2YDd"
      },
      "outputs": [],
      "source": [
        "flexible_dense.variables"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7PuNUnf0OIpF"
      },
      "source": [
        "`build`는 한 번만 호출되므로 입력 형상이 레이어의 변수와 호환되지 않으면 입력이 거부됩니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "caYWDrHSAy_j"
      },
      "outputs": [],
      "source": [
        "try:\n",
        "  print(\"Model results:\", flexible_dense(tf.constant([[2.0, 2.0, 2.0, 2.0]])))\n",
        "except tf.errors.InvalidArgumentError as e:\n",
        "  print(\"Failed:\", e)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YnporXiudF1I"
      },
      "source": [
        "Keras 레이어에는 다음과 같은 더 많은 추가 기능이 있습니다.\n",
        "\n",
        "- 선택적 손실\n",
        "- 메트릭 지원\n",
        "- 훈련 및 추론 사용을 구분하기 위한 선택적 `training` 인수에 대한 기본 지원\n",
        "- Python에서 모델 복제를 허용하도록 구성을 정확하게 저장할 수 있는 `get_config` 및 `from_config` 메서드\n",
        "\n",
        "사용자 정의 레이어에 대한 [전체 가이드](./keras/custom_layers_and_models.ipynb)에서 해당 부분을 읽어보세요."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "L2kds2IHw2KD"
      },
      "source": [
        "### Keras 모델\n",
        "\n",
        "모델을 중첩된 Keras 레이어로 정의할 수 있습니다.\n",
        "\n",
        "그러나 Keras는 `tf.keras.Model`이라는 완전한 기능을 갖춘 모델 클래스도 제공합니다. `tf.keras.layers.Layer`에서 상속되므로 Keras 모델은 Keras 레이어이며 같은 방식으로 사용, 중첩 및 저장할 수 있습니다. Keras 모델에는 쉽게 훈련, 평가, 로드 및 저장하고, 심지어 여러 머신에서 훈련할 수 있는 추가 기능이 있습니다.\n",
        "\n",
        "거의 동일한 코드로 위에서 `SequentialModule`을 정의할 수 있으며, 다시 `__call__`을 `call()` 로 변환하고 부모를 변경할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Hqjo1DiyrHrn"
      },
      "outputs": [],
      "source": [
        "class MySequentialModel(tf.keras.Model):\n",
        "  def __init__(self, name=None, **kwargs):\n",
        "    super().__init__(**kwargs)\n",
        "\n",
        "    self.dense_1 = FlexibleDense(out_features=3)\n",
        "    self.dense_2 = FlexibleDense(out_features=2)\n",
        "  def call(self, x):\n",
        "    x = self.dense_1(x)\n",
        "    return self.dense_2(x)\n",
        "\n",
        "# You have made a Keras model!\n",
        "my_sequential_model = MySequentialModel(name=\"the_model\")\n",
        "\n",
        "# Call it on a tensor, with random results\n",
        "print(\"Model results:\", my_sequential_model(tf.constant([[2.0, 2.0, 2.0]])))\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8i-CR_h2xw3z"
      },
      "source": [
        "추적 변수 및 하위 모듈을 포함하여 같은 기능을 모두 사용할 수 있습니다.\n",
        "\n",
        "참고: 위의 참고 사항을 강조하기 위해 Keras 레이어 또는 모델 내에 중첩된 원시 `tf.Module`은 훈련 또는 저장을 위해 수집된 변수를 가져오지 않습니다. 대신 Keras 레이어 내부에 Keras 레이어를 중첩합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "hdLQFNdMsOz1"
      },
      "outputs": [],
      "source": [
        "my_sequential_model.variables"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JjVAMrAJsQ7G"
      },
      "outputs": [],
      "source": [
        "my_sequential_model.submodules"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FhP8EItC4oac"
      },
      "source": [
        "`tf.keras.Model`을 재정의하는 것은 TensorFlow 모델을 빌드하는 Python다운 접근 방식입니다. 다른 프레임워크에서 모델을 마이그레이션하는 경우 매우 간단할 수 있습니다.\n",
        "\n",
        "기존 레이어와 입력을 간단하게 조합한 모델을 구성하는 경우, 모델 재구성 및 아키텍처와 관련된 추가 기능과 함께 제공되는 [함수형 API](./keras/functional.ipynb)를 사용하여 시간과 공간을 절약할 수 있습니다.\n",
        "\n",
        "다음은 함수형 API가 있는 같은 모델입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jJiZZiJ0fyqQ"
      },
      "outputs": [],
      "source": [
        "inputs = tf.keras.Input(shape=[3,])\n",
        "\n",
        "x = FlexibleDense(3)(inputs)\n",
        "x = FlexibleDense(2)(x)\n",
        "\n",
        "my_functional_model = tf.keras.Model(inputs=inputs, outputs=x)\n",
        "\n",
        "my_functional_model.summary()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "kg-xAZw5gaG6"
      },
      "outputs": [],
      "source": [
        "my_functional_model(tf.constant([[2.0, 2.0, 2.0]]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "s_BK9XH5q9cq"
      },
      "source": [
        "여기서 가장 큰 차이점은 입력 형상이 함수형 구성 프로세스의 일부로 미리 지정된다는 것입니다. 이 경우 `input_shape` 인수를 완전히 지정할 필요는 없습니다. 일부 차원은 `None`으로 남겨 둘 수 있습니다.\n",
        "\n",
        "참고: `input_shape` 또는 `InputLayer`를 하위 클래스화된 모델에 지정할 필요가 없습니다. 이들 인수와 레이어는 무시됩니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qI9aXLnaHEFF"
      },
      "source": [
        "## Keras 모델 저장하기\n",
        "\n",
        "Keras 모델에서는 체크포인트를 사용할 수 있으며 `tf.Module`과 같게 보입니다.\n",
        "\n",
        "Keras 모델은 모듈 `tf.saved_models.save()`로 저장할 수도 있습니다. 그러나 Keras 모델에는 편리한 메서드와 기타 기능이 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "SAz-KVZlzAJu"
      },
      "outputs": [],
      "source": [
        "my_sequential_model.save(\"exname_of_file\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "C2urAeR-omns"
      },
      "source": [
        "쉽게 다시 로드할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Wj5DW-LCopry"
      },
      "outputs": [],
      "source": [
        "reconstructed_model = tf.keras.models.load_model(\"exname_of_file\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EA7P_MNvpviZ"
      },
      "source": [
        "Keras `SavedModels`는 또한 메트릭, 손실 및 옵티마아저 상태를 저장합니다.\n",
        "\n",
        "이 재구성된 모델을 사용할 수 있으며 같은 데이터로 호출될 때 같은 결과를 생성합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "P_wGfQo5pe6T"
      },
      "outputs": [],
      "source": [
        "reconstructed_model(tf.constant([[2.0, 2.0, 2.0]]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xKyjlkceqjwD"
      },
      "source": [
        "기능 지원을 위한 사용자 정의 레이어 대한 구성 방법을 제공하는 것을 포함하여 Keras 모델의 저장 및 직렬화에 대해 알아야 할 정보가 더 많이 있습니다. [저장 및 직렬화 가이드](keras/save_and_serialize)를 확인하세요."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kcdMMPYv7Krz"
      },
      "source": [
        "# 다음 단계\n",
        "\n",
        "Keras에 대한 자세한 내용을 알고 싶다면, [여기](./keras/)에서 기존 Keras 가이드를 따르세요.\n",
        "\n",
        "`tf.module` 기반으로 구축된 상위 수준 API의 또 다른 예는 DeepMind의 Sonnet이며 [해당 사이트](https://github.com/deepmind/sonnet)에서 다룹니다."
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [
        "ISubpr_SSsiM"
      ],
      "name": "intro_to_modules.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
